import datetime
import random
import time

from selenium.webdriver import ActionChains

from libs.action import WebDriverAction
from libs.cache import Cache
from libs.game import Game
from libs.image import ImageTool
from libs.log import Log
from libs.movement import Movement
from libs.task_helper import TaskHelper
from libs.trade_helper import TradeHelper
from libs.window import AdsWindow


class ExceedMaxMineCount(Exception):
    pass


def do_mine(loop, is_go_up, repeat=1):
    global mine_count
    if game.get_energy() < 100:
        raise ExceedMaxMineCount

    max_mine_count = random.randint(4, 6) * 10
    game.use_backpack_item(pick_index)
    for index in range(loop):
        current_x = random.randint(100, 800)
        current_y = random.randint(100, 800)
        ac.reset_actions()
        ac.move_by_offset(current_x, current_y).perform()

        full_locs = image_tool.find_all_targets(f"mining_v2_full.png", (200, 300, 750, 950))
        for x, y in full_locs:
            ac.move_by_offset(x - current_x, y - current_y).perform()
            ac.click().perform()
            current_x = x
            current_y = y
            time.sleep(0.1)
        time.sleep(1)
        empty_locs = image_tool.find_all_targets(f"mining_v2_empty.png", (200, 300, 750, 950))
        empty_count = len(empty_locs)
        mine_count += empty_count
        for _ in range(repeat):
            for x, y in empty_locs:
                ac.move_by_offset(x - current_x, y - current_y).perform()
                ac.click().perform()
                current_x = x
                current_y = y
                time.sleep(0.1)
            if empty_count <= 1:
                break
            else:
                time.sleep(2)

        if mine_count >= max_mine_count:
            raise ExceedMaxMineCount

        move_seconds = random.randint(15, 25) / 10
        if is_go_up:
            movement.keep_move_up(move_seconds)
        else:
            movement.keep_move_down(move_seconds)


def throw_dustbin():
    time.sleep(1)
    action.escape()
    dustbin_loc = image_tool.find_target('dustbin.png')
    if dustbin_loc:
        dustbin_x, dustbin_y = dustbin_loc
        throw_items = ['Clayum Matrix', 'Marbleite Matrix', 'Ochrux Matrix', 'Copperite Ore', 'Bronzenite Ore']
        if mining_level > 20:
            throw_items.append('Basic Pick')
        game.chest_in(dustbin_x, dustbin_y,
                      throw_items)
    action.escape()
    game.use_backpack_item(pick_index)


def wait(cache_key, seconds=60):
    last_ts = cache.get(cache_key)
    current_ts = float(datetime.datetime.now().timestamp())
    if last_ts:
        last_ts = float(last_ts)
        wait_seconds = max(0, seconds - (current_ts - last_ts))
        log.debug(f'wait {wait_seconds} seconds')
        cache.set(cache_key, current_ts + wait_seconds, wait_seconds)
        time.sleep(wait_seconds)
    else:
        cache.set(cache_key, current_ts, seconds)


if __name__ == "__main__":
    parser = TaskHelper.default_args_parser('Mining')
    args = parser.parse_args()
    task_helper = TaskHelper.from_args(args)

    user_id = args.user_id

    try:
        log = Log(user_id)
        driver = AdsWindow(user_id).open(False)
        game = Game(driver, user_id)
        action = WebDriverAction(driver)
        ac = ActionChains(driver)
        movement = Movement(driver)
        image_tool = ImageTool(driver)
        trade_helper = TradeHelper(driver, user_id)
        cache = Cache()

        game.enter_game()
        game.collect_mail()
        game.snapshot(user_id, 'before mining')

        wine_name = 'Berry Blast'
        wine_qty = game.get_backpack_qty(wine_name)
        if game.get_coin() > 300000 and wine_qty < 10:
            action.goto_shop()
            trade_helper.goto_seed_store()
            time.sleep(2)
            trade_helper.open_seed_buy()
            time.sleep(1)
            trade_helper.buy_seed(wine_name, 40 - wine_qty)
            time.sleep(1)
            action.escape()
            game.get_backpack()

        fail_times = 0
        mining_level = 0
        for loop in range(40):
            try:
                game.enter_game()
                _, skills = game.get_skills()
                mining_level = skills.get('mining', 0)
                tier = 2
                if mining_level < 20:
                    tier = 1

                log.debug(f'loop={loop + 1}, energy={game.get_energy()}, mining_level={mining_level}')
                action.goto_spec()
                wait(f'mining_start_{tier}', 20)

                game.spec_drink_wine(600)

                if game.get_energy() < 100:
                    break
                action.goto_land(3451)
                game.close_backpack()

                if mining_level >= 40:
                    break

                mine_count = 0
                # 小于20级向左走
                if mining_level < 20:
                    v1_repeat = random.randint(2,3)
                    movement.keep_move_up(5)
                    movement.keep_move_up(5)
                    movement.keep_move_up(5)
                    pick_index, _ = game.get_backpack_index_and_qty('Basic Pick')
                    movement.keep_move_left(5)
                    throw_dustbin()
                else:
                    pick_index, _ = game.get_backpack_index_and_qty('Bronzenite Pick')
                    v1_repeat = 1
                    v2_repeat = random.randint(2,3)
                    movement.keep_move_up(3)
                    do_mine(6, True, v2_repeat)
                    wait('mining_step1', 10)

                    movement.keep_move_right(5)
                    movement.keep_move_right(5)
                    movement.keep_move_down(1)

                    do_mine(6, False, v2_repeat)

                    throw_dustbin()
                    wait('mining_step2', 10)

                    movement.keep_move_left(2)
                    movement.keep_move_left(2)

                    do_mine(6, True, v2_repeat)

                    movement.keep_move_left(8)
                    movement.keep_move_left(5)

                movement.keep_move_down(1)
                do_mine(5, False, v1_repeat)

                movement.keep_move_left(2)
                movement.keep_move_left(2)
                wait('mining_step3', 10)

                do_mine(5, True, v1_repeat)

                movement.keep_move_left(1)
                movement.keep_move_left(1)
                do_mine(5, False, v1_repeat)
            except ExceedMaxMineCount:
                continue
            except Exception as e:
                print(e)
                if fail_times < 2:
                    fail_times += 1
                    continue
                else:
                    raise e

        trade_helper.goto_sell_position()
        trade_helper.open_shop_sell()
        trade_helper.sell_item("Voidtonium", 111, price_delta=-10)
        trade_helper.sell_item("Gravelglass Matrix", 1111, price_delta=-1)
        for sell_name in ['Clayum Matrix', 'Marbleite Matrix', 'Ochrux Matrix', 'Copperite Ore', 'Bronzenite Ore']:
            trade_helper.sell_item(sell_name, 1111, price_delta=-1)
        action.escape()
        action.escape()

        game.get_backpack()

        game.snapshot(user_id, 'after mining')
        task_helper.task_success()
        if mining_level < 40:
            if game.get_coin() > 300000:
                task_helper.add_task('mining.py', delay_seconds=60 * 30, ignore_running=True, priority=9)
            else:
                task_helper.add_task('mining.py', delay_seconds=60 * 60 * 8, ignore_running=True, priority=9)
    except Exception as e:
        task_helper.task_fail()
        task_helper.add_task('mining.py', delay_seconds=0, ignore_running=True, retry=True)
        raise e
